package register

import (
	"errors"
	"reflect"
	"sync"
)

var tableModels map[string]*model

var (
	userMu sync.RWMutex
	rUser  = interface{}(StandarUser{})
)

var (
	ErrUserModelNotRegisted = errors.New("user model is not registed")
)

func init() {
	tableModels = make(map[string]*model)
}

type pg interface {
	TableName() string
	SchemaName() string
}

type model struct {
	checked bool
	isPG    bool
	m       interface{}
	tags    map[string]int
	cols    map[string]string
}

// Register 模型注册接口
type Register interface {
	User
	GetUserFromClause() string
	GetUserCols() map[string]string
}

// Registe 模型注册器
type Registe struct{}

// RegisterUser 注册用户模型
func (r Registe) RegisterUser(user interface{}) {
	userMu.Lock()
	defer userMu.Unlock()
	_, ok := user.(User)
	if !ok {
		panic("self:the user model must have verify method")
	}
	rv := reflect.ValueOf(user)
	if rv.Kind() == reflect.Ptr {
		rUser = rv.Elem().Interface()
	}
	rUser = user
}

// NewUser create the new user pointer
func (r Registe) NewUser() interface{} {
	userMu.RLock()
	defer userMu.RUnlock()
	return rUser
}

// // RegisterUser 注册用户模型
// func (r Registe) RegisterUser(user interface{}) error {
// 	m := model{
// 		m:    user,
// 		tags: make(map[string]int),
// 		cols: make(map[string]string),
// 	}
// 	err := checkModel(&m)
// 	if err != nil {
// 		return err
// 	}
// 	err = checkUserModel(&m)
// 	if err != nil {
// 		return err
// 	}

// 	tableModels["user"] = &m

// 	return nil
// }

func checkModel(m *model) error {
	rv := reflect.ValueOf(m.m)
	if rv.Kind() != reflect.Ptr {
		return errors.New("pointer required")
	}

	el := rv.Elem()

	m1 := el.MethodByName("TableName")
	m2 := el.MethodByName("SchemaName")
	if m1.IsValid() && m2.IsValid() {
		m.isPG = true
	} else {
		m.isPG = false
	}

	m.checked = true
	return nil
}

func checkUserModel(m *model) error {
	if !m.checked {
		panic("checkModel func not call")
	}
	rt := reflect.TypeOf(m.m).Elem()

	nf := rt.NumField()

	for i := 0; i < nf; i++ {
		f := rt.Field(i)
		st := f.Tag.Get("self")
		ot := f.Tag.Get("orm")
		switch st {
		case "id":
			{
				m.tags["id"] = i
				if ot != "" {
					m.cols["id"] = ot
				} else {
					m.cols["id"] = "id"
				}
			}
		case "name":
			{
				m.tags["name"] = i
				if ot != "" {
					m.cols["name"] = ot
				} else {
					m.cols["name"] = "name"
				}
			}
		case "tel":
			{
				m.tags["tel"] = i
				if ot != "" {
					m.cols["tel"] = ot
				} else {
					m.cols["tel"] = "tel"
				}
			}
		case "password":
			{
				m.tags["password"] = i
				if ot != "" {
					m.cols["password"] = ot
				} else {
					m.cols["password"] = "password"
				}
			}
		case "rolename":
			{
				m.tags["rolename"] = i
				if ot != "" {
					m.cols["rolename"] = ot
				} else {
					m.cols["rolename"] = "rolename"
				}
			}
		}
	}
	if len(m.tags) != 5 {
		return errors.New("user model self tag id, name, tel, password, rolename not match")
	}

	return nil

}

// ParseUser 解析use model
func (r Registe) ParseUser(user interface{}) (id int, name, tel, password, rolename string) {
	if !isRegistedUserModel(user) {
		panic("input user is not the registed user model")
	}
	m := tableModels["user"]
	rv := reflect.ValueOf(user).Elem()
	id = rv.Field(m.tags["id"]).Interface().(int)
	name = rv.Field(m.tags["name"]).Interface().(string)
	tel = rv.Field(m.tags["tel"]).Interface().(string)
	password = rv.Field(m.tags["password"]).Interface().(string)
	rolename = rv.Field(m.tags["rolename"]).Interface().(string)
	return
}

func (r Registe) SetUser(user interface{}, id int, name, tel, rolename string) {
	if !isRegistedUserModel(user) {
		panic("input user is not the registed user model")
	}
	m := tableModels["user"]
	rv := reflect.ValueOf(user).Elem()
	rv.Field(m.tags["id"]).SetInt(int64(id))
	rv.Field(m.tags["name"]).SetString(name)
	rv.Field(m.tags["tel"]).SetString(tel)
	rv.Field(m.tags["password"]).SetString("")
	rv.Field(m.tags["rolename"]).SetString(rolename)
}

func isRegistedUserModel(user interface{}) bool {
	m, ok := tableModels["user"]
	if !ok {
		panic(ErrUserModelNotRegisted)
	}
	rrt := reflect.TypeOf(m.m).Elem()
	rt := reflect.TypeOf(user).Elem()
	if rrt != rt {
		return false
	}
	return true
}

// GetUserFromClause get the user table name
func (r Registe) GetUserFromClause() string {
	m, ok := tableModels["user"]
	if !ok {
		panic("user model not registed")
	}
	if m.isPG {
		pg := m.m.(pg)
		return pg.SchemaName() + "." + pg.TableName()
	}
	return "public.user"
}

func (r Registe) GetUserCols() map[string]string {
	m, ok := tableModels["user"]
	if !ok {
		panic("user model not registed")
	}
	return m.cols
}
